#include "Sock.hpp"
#include <iostream>
#include <sys/select.h>
#define NUM sizeof(fd_set) * 8
static int fd_array[NUM];

static void Usage(std::string proc){
    std::cout << "Usage: \n\t" << proc << " port" << std::endl;
}
int main(int argc, char* argv[]){
    if (argc != 2){
        Usage(argv[0]);
        exit(1);
    }

    uint16_t port = (uint16_t)atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    for (int i = 0; i < NUM; i++) fd_array[i] = -1;
    fd_array[0] = listen_sock;
    int max_fd = listen_sock;
    // accept: 不应该，accept的本质叫做通过listen_sock获取新连接
    //         前提是listen_sock上面有新连接，accept怎么直到有新连接呢？？
    //         不知道！！！accept阻塞式等待

    // 站在多路转接的视角，我们认为链接到来，对于listen_sock,就是读事件就绪
    // 对于所有的服务器，最开始的时候只有listen_sock

    // 事件循环
    for ( ; ; ){
        fd_set rfds;     // read  fd_set
        FD_ZERO(&rfds);  // clear fd_set
        for (int i = 0; i < NUM; i++){
            if (fd_array[i] == -1) continue;

            FD_SET(fd_array[i], &rfds);
            if (max_fd < fd_array[i]){
                max_fd = fd_array[i];    // 更新最大fd
            }
        }
        FD_SET(listen_sock, &rfds);   // 将监听描述符放入fd_set
        struct timeval timeout{5, 0}; // 最大阻塞等待时间5s

        // 服务器上的所有fd（包括listen_sock）,都要交给select进行检测！！
        // recv,read,write,send,accept 等接口只负责自己最核心的工作：真正的读写（listen_sock 进行真正的获取accept）

        int n = select(max_fd + 1, &rfds, nullptr, nullptr, nullptr); // 暂时使用阻塞
        switch(n){
            case -1: std::cerr << "select error" << std::endl; break;
            case 0:  std::cout << "select timeout" << std::endl; break;
            default: std::cout << "有fd对应的事件就绪了" << std::endl; 
            for (int i = 0; i < NUM; i++){
                if (fd_array[i] == -1) continue;
                if (FD_ISSET(fd_array[i], &rfds)){
                    // 读事件就绪了！！ 就绪的fd就在fd_array[i]中保存
                    // 此时调用read, recv, accept等接口一定不会被阻塞
                    std::cout << "sock: " << fd_array[i] << " 上面有了读事件，可以读取了" << std::endl;
                    if (fd_array[i] == listen_sock){
                        // listen_sock 读就绪，调用accept
                        std::cout << "listen_sock: " << listen_sock << " 有了新的连接到来" << std::endl;
                        int sock = Sock::Accept(listen_sock);
                        if (sock >= 0){
                            std::cout << "listen_sock: " << listen_sock << " 获取新的连接成功" << std::endl;
                            // 获取链接成功，不能直接进行recv或者read，若被恶意链接，对方不发送信息，我们的服务器就会被挂起
                            // 所以新连接到来，并不意味着数据到来！！只有select直到那些fd上的信息可以被读取了，可以将其放入fd_array
                            int pos = 1;
                            for (; pos < NUM; pos++){
                                if (fd_array[pos] == -1) break;
                            }
                            // 1.找到一个位置没有被使用
                            if(pos < NUM){ fd_array[pos] = sock; std::cout << "新连接: " << sock << " 已经被添加到了数组[" << pos << "]的位置" << std::endl; }
                            // 2.所有位置全部被占用了，服务器已经满载，没法处理新的请求了
                            else { close(sock); std::cout << "服务器已经满载了，关闭新的连接" << std::endl; }
                        }
                    }
                    else {
                        // 普通的sock，读事件就绪了，可以进行读取了
                        // 可是，数据已经完全准备好了吗？？有没有可能数据只发了一半呢？？就没有所谓的数据包粘包问题吗？？
                        // 今天我们没有场景不能解决，没有场景就无法定制协议。
                        std::cout << "sock: " << fd_array[i] << " 上面有普通读取" << std::endl;
                        char recv_buffer[1024] = {0};
                        ssize_t s = recv(fd_array[i], recv_buffer, sizeof(recv_buffer) - 1, 0); 
                        if (s > 0) {           // 读取成功
                            recv_buffer[s] = 0;
                            std::cout << "client[" << fd_array[i] << "]# " << recv_buffer << std::endl; 
                        }
                        else if (s == 0) {     // 对端关闭连接
                            std::cout << "sock:" << fd_array[i] << "客户端推出了" << std::endl;
                            close(fd_array[i]);
                            std::cout << "已经在数组fd_array[" << i << "]中,去掉了sock: " << fd_array[i];
                            fd_array[i] = -1;
                        }
                        else {                 // 读取失败
                            std::cerr << "recv errno" << errno << std::endl;
                        }
                    }
                }

            }
            break;
        }
    }
    return 0;
}